package com.booter.common;

import org.apache.http.HeaderElement;
import org.apache.http.HeaderElementIterator;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.protocol.HTTP;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import java.security.NoSuchAlgorithmException;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;

public class HttpConnectionManager {

    private static Logger logger = LoggerFactory.getLogger(HttpConnectionManager.class);

    private PoolingHttpClientConnectionManager cm;

    private CloseableHttpClient httpClient;
    private int connectionRequestTimeout = 30 * 1000;
    private int connectTimeout = 10 * 1000;
    private int socketTimeout = 60 * 1000;
    private int retry = 3;
    private Timer timer;


    public void init() {
        LayeredConnectionSocketFactory sslsf = null;
        try {
            sslsf = new SSLConnectionSocketFactory(SSLContext.getDefault());
        } catch (NoSuchAlgorithmException e) {
            logger.error("HttpConnectionManager init error:", e);
        }

        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder
                .<ConnectionSocketFactory>create().register("https", sslsf)
                .register("http", new PlainConnectionSocketFactory()).build();
        this.cm = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        this.cm.setMaxTotal(300);
        this.cm.setDefaultMaxPerRoute(30);

        this.httpClient = HttpClients.custom()
                .setDefaultRequestConfig(this.getDefaultRequestConfig())
                //.setRetryHandler(new CustomHttpRequestRetryHandler(this.retry))
                //.setServiceUnavailableRetryStrategy(new CustomServiceUnavailableRetryStrategy(this.retry))
                //默认重试设置
                .setRetryHandler(new DefaultHttpRequestRetryHandler(this.retry, true))  // 重试次数
                .setConnectionManager(this.cm)
//                .disableAutomaticRetries() 禁用重试
                .setKeepAliveStrategy(connectionKeepAliveStrategy())
                .build();

        timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                cm.closeExpiredConnections();
                cm.closeIdleConnections(5, TimeUnit.SECONDS);
            }
        }, 0, 5 * 1000);
    }

    public CloseableHttpClient getHttpClient() {
        return this.httpClient;
    }

    private RequestConfig getDefaultRequestConfig() {
        return RequestConfig.custom()
                .setConnectionRequestTimeout(this.connectionRequestTimeout)
                .setConnectTimeout(this.connectTimeout)
                .setSocketTimeout(this.socketTimeout).build();
    }

    public void setConnectionRequestTimeout(int connectionRequestTimeout) {
        this.connectionRequestTimeout = connectionRequestTimeout;
    }

    public void setConnectTimeout(int connectTimeout) {
        this.connectTimeout = connectTimeout;
    }

    public void setSocketTimeout(int socketTimeout) {
        this.socketTimeout = socketTimeout;
    }

    public void setRetry(int retry) {
        this.retry = retry;
    }


    /**
     * 长连接策略
     *
     * @return
     */
    private ConnectionKeepAliveStrategy connectionKeepAliveStrategy() {
        //new ConnectionKeepAliveStrategy()
        return (response, httpContext) -> {
            HeaderElementIterator it = new BasicHeaderElementIterator
                    (response.headerIterator(HTTP.CONN_KEEP_ALIVE));
            while (it.hasNext()) {
                HeaderElement he = it.nextElement();
                String param = he.getName();
                String value = he.getValue();
                if (value != null && param.equalsIgnoreCase("timeout")) {
                    return Long.parseLong(value) * 1000;
                }
            }
            //60s
            return 60_000;
        };
    }


}